using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Package;
using System.CodeDom.Compiler;
using NBusiness.CodeDom;
using NBusiness.ESharp;
using NBusiness.CodeDom.Compiler;
using NBusiness.ESharp.Compiler;

namespace NBusiness.VisualStudio.Language
{
    public class ESharpScanner : IScanner, IDisposable
    {
        string _source;
        int _offset;
        ESharpLanguageService _language;
        EntityRoot _root;

        public ESharpScanner(ESharpLanguageService language)
        {
            _language = language;
            _root = language.GetCurrentRoot();
        }

        #region IScanner Members

        public bool ScanTokenAndProvideInfoAboutIt(TokenInfo tokenInfo, ref int state)
        {
            bool more = false;
            state = 0;

            List<EntityElement> elements = new List<EntityElement>(ESharpParser.GetElements(_source));
            if (elements.Count > 0)
            {
                EntityElement element = elements.Find(new Predicate<EntityElement>(delegate(EntityElement e)
                {
                    return _offset >= e.BeginOffset && _offset <= e.EndOffset;
                }));

                if (element != null)
                {
                    more = true;

                    tokenInfo.Color = TokenColor.Text;
                    tokenInfo.StartIndex = element.BeginOffset;
                    tokenInfo.EndIndex = element.EndOffset;
                    tokenInfo.Type = GetTokenType(element);
                    
                    _offset = element.EndOffset+1;

                    switch (tokenInfo.Type)
                    {
                        case TokenType.WhiteSpace:
                            tokenInfo.Trigger |= TokenTriggers.MemberSelect;
                            break;
                        case TokenType.Unknown:
                            {
                                if(ESharpParser.Expressions.GroupBegin.IsMatch(element.Value) ||
                                    ESharpParser.Expressions.GroupEnd.IsMatch(element.Value))
                                    tokenInfo.Trigger |= TokenTriggers.MatchBraces;
                                break;
                            }
                        case TokenType.LineComment:
                        case TokenType.Comment:
                            {
                                tokenInfo.Color = TokenColor.Comment;
                                break;
                            }
                        case TokenType.Identifier:
                            {   
                                tokenInfo.Color = TokenColor.Identifier;
                                tokenInfo.Trigger = TokenTriggers.MethodTip;
                                break;
                            }
                        case TokenType.Delimiter:
                            {
                                tokenInfo.Trigger |= TokenTriggers.MemberSelect;
                                break;
                            }
                        case TokenType.Keyword:
                            {
                                tokenInfo.Color = TokenColor.Keyword;
                                break;
                            }
                        case TokenType.String:
                            {
                                tokenInfo.Color = TokenColor.String;
                                break;
                            }
                        case TokenType.Literal:
                            {
                                tokenInfo.Color = TokenColor.Number;
                                break;
                            }
                        case TokenType.Text:
                        case TokenType.Operator:
                            break;
                    }
                }
            }
            return more;
        }

        private TokenType GetTokenType(EntityElement element)
        {
            TokenType token = TokenType.Unknown;
            if (ESharpParser.Expressions.WhiteSpace.IsMatch(element.Value))
                token = TokenType.WhiteSpace;
            else if (ESharpParser.Expressions.Keywords.IsMatch(element.Value))
                token = TokenType.Keyword;
            else if (ESharpParser.Expressions.Delimiter.IsMatch(element.Value))
                token = TokenType.Delimiter;
            else if (ESharpParser.Expressions.LineComments.IsMatch(element.Value))
                token = TokenType.LineComment;
            else if(ESharpParser.Expressions.Literal.IsMatch(element.Value))
                token = TokenType.Literal;
            else if(ESharpParser.Expressions.Quoted.IsMatch(element.Value))
                token = TokenType.String;
            else if (ESharpParser.Expressions.Identifier.IsMatch(element.Value))
            {
                bool found = false;
                foreach (EntityFamily f in _root.Families.Values)
                {
                    if( FindIdentifier(f, element.Value))
                    {
                        found = true;
                        break;
                    }
                }
                if (found) token = TokenType.Identifier;
                else token = TokenType.Text;
            }
            return token;
        }

        private bool FindIdentifier(EntityFamily family, string name)
        {
            bool found = (family.Name == name);

            if(!found)
            {
                foreach (EntityFamily f in family.Families.Values)
                {
                    found = FindIdentifier(f, name);
                }

                foreach (Entity e in family.Entities)
                {
                    if (e.Name == name)
                    {
                        found = true;
                        break;
                    }
                }
            }

            return found;
        }

        public void SetSource(string source, int offset)
        {
            _source = source;
            _offset = offset;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            // TODO: Implement this!
        }

        #endregion
    }
}
